package com.benshou.bsoa.service.yida;


import com.alibaba.fastjson2.JSON;
import com.aliyun.dingtalkyida_2_0.models.SearchFormDataSecondGenerationResponse;
import com.aliyun.dingtalkyida_2_0.models.SearchFormDatasResponse;
import com.aliyun.dingtalkyida_2_0.models.SearchFormDatasResponseBody;

import com.benshou.bsoa.constants.SearchConstants;
import com.benshou.bsoa.dto.feikong.req.PaymentRequestData;
import com.benshou.bsoa.dto.feikong.req.ReimbursementRequestData;
import com.benshou.bsoa.dto.feikong.res.PaymentResponse;
import com.benshou.bsoa.dto.feikong.res.ReimbursementResponse;
import com.benshou.bsoa.dto.renshi.req.EmployeeDataRequest;
import com.benshou.bsoa.dto.renshi.res.EmployeeDataResponse;
import com.benshou.bsoa.dto.xinchou.req.SalaryAccountRequest;
import com.benshou.bsoa.dto.xinchou.res.SalaryAccountResponse;
import com.benshou.bsoa.dto.yida.req.YiDaReq;
import com.benshou.bsoa.dto.yida.analysis.OtherCost;
import com.benshou.bsoa.dto.yida.analysis.ProjectAccount;
import com.benshou.bsoa.dto.yida.analysis.ProjectMember;
import com.benshou.bsoa.dto.yida.req.YiDaSearchReq;
import com.benshou.bsoa.service.dingding.AttendService;
import com.benshou.bsoa.dto.yida.analysis.CovertProjectToFromDataJson;
import com.benshou.bsoa.dto.yida.analysis.TransFormDataJsonToProject;
import com.benshou.bsoa.service.dingding.EmployeeDataService;
import com.benshou.bsoa.service.dingding.SalaryArchiveService;
import com.benshou.bsoa.service.feikong.FeiKongService;
import com.benshou.bsoa.thread.ThreadPoolUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.benshou.bsoa.constants.CostConstants.*;
import static com.benshou.bsoa.constants.ProjectAnalysistFieldConstants.*;

/*
 * 同步项目成本信息
 * 1.获取项目台账信息
 * 2.获取字表项目成员信息
 * 3.获取项目薪酬数据
 * 4.计算成员个人成本
 */
@Service
public class SynchronousProjectAnalysisService {

    private static final Logger log = LoggerFactory.getLogger(SynchronousProjectAnalysisService.class);

    @Autowired
    private GetYiDaService getYiDaService;
    @Autowired
    private PostYiDaService postYiDaService;
    @Autowired
    private TransFormDataJsonToProject transFormDataJsonToProjectUtil;
    @Autowired
    private CovertProjectToFromDataJson covertProjectToFromDataJson;
    @Autowired
    private SalaryArchiveService salaryArchiveService;
    @Autowired
    private EmployeeDataService employeeDataService;
    @Autowired
    private UpdateYiDaService updateYiDaService;
    @Autowired
    private SearchYiDaService searchYiDaService;
    @Autowired
    private FeiKongService feiKongService;

    @Value("${feikong.account}")
    private String qybh; // 企业编号,根据费控云创建的企业编号固定传值

    @Value("${yida.formuuid.analysis}")
    private String formUuidAnalysisUUID; // 表单UUID

    /**
     * 主流程：同步项目信息和成本数据到钉钉表单
     */
    public int synchronousProjectAnalysisService(YiDaReq yiDaReq) throws Exception {
        // 第一步：获取项目台账数据
        List<ProjectAccount> projects = fetchProjectAccounts(yiDaReq);

        // 第二步：创建线程池
        ExecutorService pool = ThreadPoolUtils.createThreadPool(
                8,      //核心线程数
                200,        // 最大线程数
                100        // 线程存活时间（秒）
        );
        // 第三步：使用 CountDownLatch 等待所有项目任务完成
        CountDownLatch latch = new CountDownLatch(projects.size());
        for (ProjectAccount account : projects) {
            pool.submit(() -> {
                try {
                    enrichMembersWithCost(account);        // 填充项目成员成本（薪资成本）
                    addMockOtherCosts(account);            // 构建成本支出
                    submitProjectCostData(account, yiDaReq); // 转为表单数据并提交
                } catch (Exception e) {
                    // 日志记录异常，不影响其他任务
                    log.error("处理项目失败: " + account.getProjectCode() + ", 错误: " + e.getMessage());
                    e.printStackTrace();
                } finally {
                    latch.countDown();
                }
            });
        }
        // 等待所有任务完成
        latch.await();
        pool.shutdown(); //关闭线程池
        return 0;
    }

    /**
     * 获取项目台账表单数据并转为项目对象
     */
    private List<ProjectAccount> fetchProjectAccounts(YiDaReq yiDaReq) throws Exception {
        List<ProjectAccount> allProjects = new ArrayList<>();
        int currentPage = 1;
        int pageSize = 100; // 宜搭接口最大支持的条数

        while (true) {
            yiDaReq.setCurrentPage(currentPage);
            yiDaReq.setPageSize(pageSize);
            SearchFormDatasResponse response = getYiDaService.queryListFormData(yiDaReq);
            List<SearchFormDatasResponseBody.SearchFormDatasResponseBodyData> dataList = response.getBody().getData();
            if (dataList == null || dataList.isEmpty()) {
                break; // 没有更多数据
            }
            // 转换为 ProjectAccount，并过滤“项目结项”
            List<ProjectAccount> pageProjects = dataList.stream()
                    .filter(data -> {
                        boolean isClosed = "项目结项".equals(data.getFormData().get(PROJECT_STATUS));
                        if (isClosed) {
                            log.info("跳过处理已结项的项目: {}", data.getFormData());
                        }
                        return !isClosed;
                    })
                    .map(data -> {
                        String formInstanceId = data.getFormInstanceId();
                        Map<String, ?> formData = data.getFormData();
                        ProjectAccount account = transFormDataJsonToProjectUtil.mapFormDataToEntity(formData);
                        account.setFormInstanceId(formInstanceId);
                        //log.info("转化的表单数据: {}", account);
                        return account;
                    })
                    .toList();

            allProjects.addAll(pageProjects);

            if (dataList.size() < pageSize) {
                break; // 最后一页
            }
            currentPage++;
        }
        return allProjects;
    }


    /*
     *  填充项目成员的薪资成本数据
     */
        private void enrichMembersWithCost(ProjectAccount account) {
                try {
                    populateSalaryCost(account.getMembers());   // 填充薪资成本数据
                } catch (Exception e) {
                    log.error("处理成员成本失败: {}", e);
            }
        }

    /**
     * 主调用方法：
     * 根据传入的项目成员，填充其对应的上上个月薪资成本及成本时间戳。
     *
     * @param memberList 项目成员
     * @throws Exception 处理过程中发生的异常
     */
    private void populateSalaryCost(List<ProjectMember> memberList) throws Exception {
        // 请求员工数据
        EmployeeDataRequest employeeDataRequest = new EmployeeDataRequest();
        EmployeeDataResponse employeeDataResponse = employeeDataService.fetchEmployeeData(employeeDataRequest);

        // 构建userId -> uuid映射
        Map<String, Long> userIdToUuidMap = buildUserIdToUuidMap(employeeDataResponse);

        // 获取成员对应UUID
        List<Long> uuids = getUuidsByMembers(memberList, userIdToUuidMap);

        // 如果uuid不为空，查询薪资并填充成员信息
        if (uuids != null) {
            setSalaryCostForMember(memberList, uuids);
        }
    }
    /**
     * 根据员工数据响应，构建 钉钉用户ID(userId) 到系统唯一UUID的映射。
     * 解决多个相同userId的冲突时，保留第一个UUID。
     *
     * @param employeeDataResponse 员工数据响应对象
     * @return userId -> uuid 的映射关系
     */
    private Map<String, Long> buildUserIdToUuidMap(EmployeeDataResponse employeeDataResponse) {
        return employeeDataResponse.getResult()
                .getList()
                .getDingdingUserId()
                .stream()
                .collect(Collectors.toMap(
                        EmployeeDataResponse.DingdingUserId::getUserid,   // 映射key：钉钉用户ID
                        EmployeeDataResponse.DingdingUserId::getUuid,     // 映射value：系统UUID
                        (uuid1, uuid2) -> uuid1                            // 重复key时保留第一个UUID
                ));
    }

    /**
     * 遍历所有项目成员，提取第一个钉钉用户ID，查找对应的 UUID。
     * 返回所有成功映射到 UUID 的列表。
     *
     * @param memberList 当前项目成员
     * @param userIdToUuidMap 钉钉用户ID到UUID的映射关系
     * @return 成员对应的 UUID 列表
     */
    private List<Long> getUuidsByMembers(List<ProjectMember> memberList, Map<String, Long> userIdToUuidMap) {
        List<Long> uuidList = new ArrayList<>();
        for (ProjectMember member : memberList) {
            if (member.getUserId() != null && !member.getUserId().isEmpty()) {
                String dingUserId = member.getUserId().get(0);  // 获取成员的钉钉ID
                Long uuid = userIdToUuidMap.get(dingUserId);
                if (uuid != null) {
                    uuidList.add(uuid);
                }
            }
        }
        return uuidList;
    }

    /**
     * 根据UUID和当前时间的上上个月，查询薪资账户数据，
     * 并将实发工资及对应的成本时间戳设置到成员对象中。
     *
     * @param member 需要填充薪资信息的项目成员
     * @param uuids 员工的系统唯一标识UUID
     * @throws Exception 查询薪资接口异常时抛出
     */
    private void setSalaryCostForMember(List<ProjectMember> member, List<Long> uuids) throws Exception {
        BigDecimal totalPaid = BigDecimal.ZERO;

        // 计算上上个月的日期，比如现在是5月，则查询4月的薪资
        LocalDate lastMonth = LocalDate.now().minusMonths(1);
        if (lastMonth.getMonthValue() == 1) {
            lastMonth = lastMonth.minusYears(1).withMonth(12);
        }
        String salaryYmForQuery = lastMonth.format(DateTimeFormatter.ofPattern("yyyy-MM"));

        // 每页最多200个 UUID
        int pageSize = 200;

        for (int i = 0; i < uuids.size(); i += pageSize) {
            List<Long> subUuids = uuids.subList(i, Math.min(i + pageSize, uuids.size()));

            SalaryAccountRequest request = new SalaryAccountRequest();
            SalaryAccountRequest.Params params = new SalaryAccountRequest.Params();

            // 设置分页参数
            params.setEmployeeIdList(subUuids);
            params.setPageNum(1); // 接口中 pageNum 实际不重要，因为每次只查一页
            params.setPageSize(subUuids.size());
            params.setSalaryYearAndMonth(salaryYmForQuery);

            request.setParams(params);

            // 调用薪资档案服务，获取薪资数据
            SalaryAccountResponse salaryAccountResponse = salaryArchiveService.fetchSalaryAccount(request);

            if (salaryAccountResponse.getResult() != null && !salaryAccountResponse.getResult().getList().isEmpty()) {
                for (SalaryAccountResponse.Result.DataItem dataItem : salaryAccountResponse.getResult().getList()) {
                    for (SalaryAccountResponse.Result.DataItem.SalaryAccount salaryAccount : dataItem.getSalaryAccountList()) {
                        String paidWages = salaryAccount.getPaidWages();
                        if (paidWages != null && !paidWages.isEmpty()) {
                            try {
                                String cleanSalary = paidWages.replace(",", "");
                                BigDecimal salary = new BigDecimal(cleanSalary);
                                totalPaid = totalPaid.add(salary);
                            } catch (NumberFormatException e) {
                                log.warn("薪资格式错误：{}", paidWages, e);
                            }
                        }
                    }
                }
            }
        }
        // 计算成本归属时间（本地时区）
        long costDateMillis = lastMonth
                .with(TemporalAdjusters.firstDayOfMonth())
                .atStartOfDay(ZoneId.systemDefault())
                .toInstant()
                .toEpochMilli();
        // 构造最终结果
        ProjectMember newMember = new ProjectMember();
        newMember.setSalaryCost(totalPaid.toPlainString());
        newMember.setCostDate(costDateMillis);
        newMember.setTotalMembers(member.size());
        // 只保留这一次member
        member.clear();
        member.add(newMember);
    }


    /**
     * 添加其他成本项
     * 报销单
     * 付款单
     */
    private void addMockOtherCosts(ProjectAccount account) {
        //获取项目的项目编码/
        String projectCode = account.getProjectCode();
        //获取项目的开始日期 格式化时间
        String startDate = LocalDate.ofInstant(Instant.ofEpochMilli(account.getPlanStartDate()), ZoneId.systemDefault())
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        //获取当前项目执行时间
        String nowDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        ReimbursementRequestData requestData = new ReimbursementRequestData();
        // 设置查询参数
        requestData.setQybh(qybh);
        //requestData.setKsrq(startDate);
        //requestData.setJsrq(nowDate);
        requestData.setKsrq("2025-02-28");
        requestData.setJsrq("2025-05-28");
        requestData.setXmdh(projectCode); // 设置项目编码
        requestData.setType("7"); //日期类型：1.提交时间 2.审批完成时间 7.创建时间
        ReimbursementResponse reimbursementResponse = feiKongService.queryReimbursement(requestData);
        List<OtherCost> otherCostList = new ArrayList<>();

        // 获取项目报销单的列表数据
        List<ReimbursementResponse.Result.FkBxdAndFymx> fkBxdAndFymxList = reimbursementResponse.getResult().getFkBxdAndFymxList();
        if (fkBxdAndFymxList == null || fkBxdAndFymxList.isEmpty()) {
            log.info("未查询到项目 {} 的报销单数据", projectCode);
            return; // 如果没有报销单数据，直接返回
        }
        fkBxdAndFymxList.forEach( fkBxdAndFymx -> {
                // 获取费用明细列表
                List<ReimbursementResponse.Result.FkBxdAndFymx.Fymx> fymxList = fkBxdAndFymx.getFymxList();
                fymxList.forEach(fymx -> {
                    OtherCost otherCost = new OtherCost();
                    //项目编码
                    otherCost.setProjectName(account.getProjectName());
                    //费用支出名称
                    otherCost.setCostName(fymx.getFylxmc());
                    //支出时间
                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                    LocalDateTime localDateTime = LocalDateTime.parse(fymx.getXfsj(), formatter);
                    long timestampMillis = localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    otherCost.setCostDate(timestampMillis);
                    //支出金额
                    otherCost.setCostAmount(fymx.getBxje());
                    otherCostList.add(otherCost);
                });
        });

        //获取项目付款单的列表数据
        PaymentRequestData paymentRequestData = new PaymentRequestData();
        paymentRequestData.setXmbh(projectCode); // 设置项目编码
        paymentRequestData.setStartTime(startDate); //开始时间
        paymentRequestData.setEndTime(nowDate); //结束时间
        paymentRequestData.setDateType("7"); // 企业编号
        paymentRequestData.setCurrent("1"); // 当前页
        paymentRequestData.setSize("100"); // 每页大小
        PaymentResponse paymentResponse = feiKongService.queryPayment(paymentRequestData);

        PaymentResponse.Result result = paymentResponse.getResult();
        //对付款单数据进行处理
        if (result != null && !result.getRecords().isEmpty()) {
            for (PaymentResponse.Result.FkdListApiBean record : result.getRecords()) {
                record.getFkdDetailList().forEach( detail -> {
                    // 创建其他成本对象
                    OtherCost otherCost = new OtherCost();
                    // 设置项目名称
                    otherCost.setProjectName(account.getProjectName());
                    // 设置费用支出名称
                    otherCost.setCostName(detail.getFylxmc());
                    // 设置支出时间
                    long timestampMillis = LocalDateTime.parse(record.getCjsj(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
                            .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    otherCost.setCostDate(timestampMillis);
                    // 设置支出金额
                    otherCost.setCostAmount(detail.getYbsqje());

                    otherCostList.add(otherCost);
                });
            }
        }
        // 将其他成本列表设置到项目对象中
        account.setOtherCosts(otherCostList);
    }

    /**
     * 提交或更新项目成本数据到钉钉表单
     */
    private void submitProjectCostData(ProjectAccount account, YiDaReq originalReq) throws Exception {
        // 将项目对象转换为表单格式
        Map<String, Object> formMap = covertProjectToFromDataJson.convertProjectAccountToFormData(account);
        // 初始化薪资子表
        initSalaryTableIfAbsent(formMap);

        // 构建查询请求，尝试获取已有表单
        YiDaSearchReq searchReq = buildSearchReq(originalReq.getUserId(), account.getProjectCode());
        SearchFormDataSecondGenerationResponse searchResult = searchYiDaService.searchFormData(searchReq);

        // 合并旧表单中的薪资数据（如果存在）
        if (hasExistingForm(searchResult)) {
            mergeOldSalaryTableData(formMap, searchResult);
        }

        // 计算总成本（薪资 + 其他成本）
        BigDecimal totalCost = calculateTotalCost(formMap, account);

        // 将总成本、盈亏信息写入表单字段
        enrichFormMapWithCostInfo(formMap, totalCost, account.getBudgetCost());

        // 构建基础钉钉请求
        YiDaReq req = buildBaseYiDaReq(originalReq.getUserId());
        req.setFormDataJson(JSON.toJSONString(formMap));

        if (hasExistingForm(searchResult)) {
            // 更新已有表单数据
            req.setFormInstanceId(getFormInstanceId(searchResult));
            updateYiDaService.updateFormData(req);
            log.info("匹配到已有表单，更新表单实例 ID: {}", req.getFormInstanceId());
        } else {
            // 创建新表单数据
            postYiDaService.saveFormData(req);
            log.info("未匹配到表单记录，创建新表单实例");
        }
    }

    /**
     * 如果表单中未包含薪资子表，则初始化为空列表
     */
    private void initSalaryTableIfAbsent(Map<String, Object> formMap) {
        if (formMap.get(PROJECT_SALARY_TABLE_NAME) == null) {
            formMap.put(PROJECT_SALARY_TABLE_NAME, new ArrayList<Map<String, Object>>());
        }
    }

    /**
     * 构建基础钉钉请求（包含表单UUID和用户ID）
     */
    private YiDaReq buildBaseYiDaReq(String userId) {
        YiDaReq req = new YiDaReq();
        req.setFormUuid(formUuidAnalysisUUID);
        req.setUserId(userId);
        return req;
    }

    /**
     * 判断搜索结果中是否存在表单记录
     */
    private boolean hasExistingForm(SearchFormDataSecondGenerationResponse searchResult) {
        return searchResult != null &&
                searchResult.getBody() != null &&
                searchResult.getBody().getData() != null &&
                !searchResult.getBody().getData().isEmpty();
    }

    /**
     * 将旧表单中的薪资子表数据合并到当前表单数据中
     */
    private void mergeOldSalaryTableData(Map<String, Object> formMap, SearchFormDataSecondGenerationResponse searchResult) {
        List<Map<String, Object>> newTableData = (List<Map<String, Object>>) formMap.get(PROJECT_SALARY_TABLE_NAME);

        // 用Set存储已经存在的“月份+薪资”组合，方便快速查重
        Set<String> existingEntries = new HashSet<>();
        for (Map<String, Object> row : newTableData) {
            String month = row.getOrDefault(PROJECT_SALARY_DATETIME, "").toString();
            String pay = row.getOrDefault(PROJECT_SALARY_PAY, "").toString();
            existingEntries.add(month + "_" + pay);
        }

        for (var data : searchResult.getBody().getData()) {
            Object oldTableObj = data.getFormData().get(PROJECT_SALARY_TABLE_NAME);
            if (oldTableObj instanceof List<?>) {
                for (Object rowObj : (List<?>) oldTableObj) {
                    if (rowObj instanceof Map<?, ?>) {
                        Map<String, Object> oldRow = new HashMap<>();
                        ((Map<?, ?>) rowObj).forEach((k, v) -> oldRow.put(String.valueOf(k), v));
                        String oldMonth = oldRow.getOrDefault(PROJECT_SALARY_DATETIME, "").toString();
                        String oldPay = oldRow.getOrDefault(PROJECT_SALARY_PAY, "").toString();
                        String key = oldMonth + "_" + oldPay;
                        // 如果不存在相同“月份+薪资”则添加
                        if (!existingEntries.contains(key)) {
                            newTableData.add(oldRow);
                            existingEntries.add(key);
                        }
                    }
                }
            }
        }
    }

    /**
     * 计算总成本 = 薪资子表总和 + 其他成本总和
     */
    private BigDecimal calculateTotalCost(Map<String, Object> formMap, ProjectAccount account) {
        BigDecimal total = BigDecimal.ZERO;
        List<Map<String, Object>> newTableData = (List<Map<String, Object>>) formMap.get(PROJECT_SALARY_TABLE_NAME);

        for (Map<String, Object> row : newTableData) {
            Object val = row.get(PROJECT_SALARY_PAY);
            if (val != null) {
                try {
                    total = total.add(new BigDecimal(val.toString()));
                } catch (NumberFormatException e) {
                    log.warn("薪资格式错误：{}", val);
                }
            }
        }
        for (OtherCost cost : account.getOtherCosts()) {
            String amount = cost.getCostAmount();
            if (amount != null && !amount.isEmpty()) {
                try {
                    total = total.add(new BigDecimal(amount));
                } catch (NumberFormatException e) {
                    log.warn("其他成本格式错误：{}", amount);
                }
            }
        }
        return total;
    }

    /**
     * 将实际成本、预算对比结果写入表单字段中
     */
    private void enrichFormMapWithCostInfo(Map<String, Object> formMap, BigDecimal total, double budget) {
        formMap.put(ACTUAL_COST, total.toPlainString());
        BigDecimal budgetCost = BigDecimal.valueOf(budget);
        if (total.compareTo(budgetCost) > 0) {
            formMap.put(COST_STATUS, COST_STATUS_OVER);
            formMap.put(COST_ERAN_MONEY, COST_ERAN_MONEY_LOSS);
        } else {
            formMap.put(COST_STATUS, COST_STATUS_NORMAL);
            formMap.put(COST_ERAN_MONEY, COST_ERAN_MONEY_PROFIT);
        }
    }

    /**
     * 获取匹配到的表单实例ID
     */
    private String getFormInstanceId(SearchFormDataSecondGenerationResponse searchResult) {
        return searchResult.getBody().getData().get(0).getFormInstanceId();
    }

    /*
     * 构建搜索请求
     */
    private YiDaSearchReq buildSearchReq(String userId, String projectCode) throws Exception {
        // 构建搜索请求
        Map<String, Object> condition = new HashMap<>();
        condition.put(SearchConstants.KEY, PROJECT_CODE);
        condition.put(SearchConstants.VALUE, projectCode);
        condition.put(SearchConstants.TYPE, SearchConstants.TEXT_TYPE);
        condition.put(SearchConstants.OPERATOR, SearchConstants.LIKE_OPERATOR);
        condition.put(SearchConstants.COMPONENT_NAME, SearchConstants.TEXT_FIELD_COMPONENT);
        // 将条件封装到请求对象中
        List<Map<String, Object>> conditionList = Collections.singletonList(condition);
        YiDaSearchReq req = new YiDaSearchReq(userId,formUuidAnalysisUUID);
        req.setSearchCondition(JSON.toJSONString(conditionList));
        return req;
    }
}

